package org.massi.imslp.query;


import java.io.StringReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sourceforge.jwbf.core.actions.Get;
import net.sourceforge.jwbf.core.actions.util.HttpAction;
import net.sourceforge.jwbf.mediawiki.actions.queries.TitleQuery;
import net.sourceforge.jwbf.mediawiki.actions.util.VersionException;
import net.sourceforge.jwbf.mediawiki.bots.MediaWikiBot;

import org.apache.log4j.Logger;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;


/**
 * Wraps a query request to the Wiki bot: it performs a get request to the API seach page, using a
 * page title as query parameer. The Title can contain wild cards. The search result (in XML format)
 * is then processed by the {@link #parseArticleTitles(String)} method using JDOM and Xpath
 */
public final class CustomQuery extends TitleQuery<String>
{
	/**
	 * class logger
	 */
	private static final Logger LOG = Logger.getLogger(CustomQuery.class);

	/**
	 * page title to look for: can contain wild-cards
	 */
	private final String pageTitle;

	/**
	 * reference to the wiki bot
	 */
	private final MediaWikiBot bot;

	/**
	 * Pattern to find out whether the search has more results
	 */
	private static final Pattern HAS_MORE_PATTERN = Pattern
			.compile(
					"<query-continue>.*?<allpages *apfrom=\"([^\"]*)\" */>.*?</query-continue>",
					Pattern.DOTALL | Pattern.MULTILINE);


	/**
	 * Constructor with bot instance and the page title
	 * 
	 * @param bot the Wiki instance
	 * @param pageTitle page title: can contain wild cards
	 * @throws VersionException if something goes wrong
	 */
	public CustomQuery(final MediaWikiBot bot, final String pageTitle) throws VersionException
	{
		super(bot);
		this.pageTitle = pageTitle;
		this.bot = bot;
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see net.sourceforge.jwbf.mediawiki.actions.queries.TitleQuery#prepareCollection()
	 */
	@Override
	protected HttpAction prepareCollection()
	{
		return generateRequest();
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * net.sourceforge.jwbf.mediawiki.actions.queries.TitleQuery#parseArticleTitles(java.lang.String
	 * )
	 */
	@Override
	protected Collection<String> parseArticleTitles(final String s)
	{
		final Collection<String> c = new ArrayList<String>();
		final SAXBuilder builder = new SAXBuilder();
		try
		{
			final Document document = builder.build(new StringReader(s));
			final XPath xPath = XPath.newInstance("/api/query/search/p/@title");
			@SuppressWarnings("unchecked")
			final List<Attribute> selectNodes = xPath.selectNodes(document);
			for (final Attribute element : selectNodes)
			{
				final String title = element.getValue();
				LOG.info(MessageFormat.format("Found title [{0}]", title));
				c.add(title);
			}
		}
		catch (final Exception e)
		{
			throw new RuntimeException(e);
		}
		return c;
	}


	@Override
	protected String parseHasMore(final String s)
	{
		final Matcher matcher = HAS_MORE_PATTERN.matcher(s);
		if (matcher.find())
		{
			return matcher.group(1);
		}
		else
		{
			return "";
		}
	}


	/**
	 * Create a get request using the page title. Because the title can contain wild cards it will
	 * be encoded
	 * 
	 * @return a Get request, where the base wiki url is combined with the wiki search API page
	 */
	private Get generateRequest()
	{
		LOG.info("Generating request for page title " + this.pageTitle);
		// Example: http://imslp.org/api.php?action=query&list=search&srsearch=*Op.127*Beethoven
		final String s = "/api.php?action=query&list=search&srprop=timestamp&format=xml&srsearch="
				+ URLParamEncoder.encode(this.pageTitle);
		return new Get(s);
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#clone()
	 */
	@Override
	public CustomQuery clone() throws CloneNotSupportedException
	{
		try
		{
			return new CustomQuery(this.bot, this.pageTitle);
		}
		catch (final VersionException e)
		{
			throw new CloneNotSupportedException(e.getLocalizedMessage());
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return this.pageTitle;
	}
}
